package reliability;

import reliability.model.GraphModel;
import reliability.model.StatisticalModelingMethodResults;
import reliability.types.NetworkConditionMap;
import reliability.types.graph.*;
import reliability.types.logical_function.LogicalFunctionElement;

import javax.swing.*;
import java.util.Map;
import java.util.Random;

public class StatisticalModelingMethod extends SwingWorker<StatisticalModelingMethodResults, Integer>
{

    private NetworkConditionMap currentNetworkCondition = new NetworkConditionMap();
    private Random randomValuesGenerator = new Random();
    private GraphModel graphModel;
    private Nodes nodes;
    private Edges edges;
    private int numberOfTests;

    public StatisticalModelingMethod(GraphModel graphModel)
    {
        this.graphModel = graphModel;
        nodes = graphModel.getNodes();
        edges = graphModel.getEdges();
        numberOfTests = graphModel.getNumberOfTests();
    }

    public StatisticalModelingMethodResults doInBackground()
    {
        setProgress(0);
        return processNetwork();
    }

    private StatisticalModelingMethodResults processNetwork()
    {
        VectorOfMinimalPaths minimalPaths = graphModel.getMinimalPaths();
        if (minimalPaths == null)
        {
            return null;
        }
        GraphParser parser = new GraphParser(graphModel);
        LogicalFunctionElement logicalStructureFunction = parser.parseLogicalStructureFunction(minimalPaths);
        double networkReliability = getNetworkReliability(logicalStructureFunction);
        double accuracy = 3 * StrictMath.sqrt(networkReliability * (1 - networkReliability) / numberOfTests);

        String logicalStructureFunctionString = "F(X1,...,X" + (nodes.size() + edges.size()) + ")=" + logicalStructureFunction.toString();
        return new StatisticalModelingMethodResults(networkReliability, accuracy, logicalStructureFunctionString);
    }

    private double getNetworkReliability(LogicalFunctionElement logicalStructureFunction)
    {
        int numberOfTrueValues = 0;
        int notificationStep = Math.min(numberOfTests / 100, 1);
        int previousNotificationValue = 0;
        for (int i = 0; i < numberOfTests; i++)
        {
            if (i >= previousNotificationValue + notificationStep)
            {
                previousNotificationValue = i;
                float progressPercentage = ((float) i / (float) numberOfTests) * 100;
                setProgress(Math.round(progressPercentage));
            }
            generateNextNetworkCondition();
            if (logicalStructureFunction.evaluate(currentNetworkCondition))
            {
                numberOfTrueValues++;
            }
        }
        setProgress(100);
        return (double) numberOfTrueValues / (double) numberOfTests;
    }

    private void generateNextNetworkCondition()
    {
        for (Map.Entry<Integer, Node> entry : nodes.entrySet())
        {
            Integer key = entry.getKey();
            Node value = entry.getValue();
            double possibility = randomValuesGenerator.nextDouble();
            currentNetworkCondition.put(key, value.getState(possibility));
        }
        for (Map.Entry<Integer, Edge> entry : edges.entrySet())
        {
            Integer key = entry.getKey();
            Edge value = entry.getValue();
            double possibility = randomValuesGenerator.nextDouble();
            currentNetworkCondition.put(key, value.getState(possibility));
        }
    }

}
